/*
 * Copyright (C) 拾光分享网 
 * All rights reserved, Designed By www.hiai.top.
 * Email：goodsking@163.com.
 * Version：V1.0.2(20230701).
 */
package top.hiai.login;

import com.jfinal.kit.HashKit;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.ehcache.CacheKit;
import top.hiai.common.constant.AppConstants;
import top.hiai.common.model.Session;
import top.hiai.common.model.User;

import java.io.IOException;
import java.util.Date;
import java.util.Random;

/**
 * @Author www.hiai.top
 * @Email goodsking@163.com
 * @Message 更多资源尽在www.hiai.top
 * @Introduction 类的介绍
 */

public class LoginService {
    private User userDao = new User().dao();
    public static final LoginService me = new LoginService();
    // 存放登录用户的 cacheName
    public static final String loginAccountCacheName = "loginAccount";
    /**
     * "jfinalId" 仅用于 cookie 名称，其它地方如 cache 中全部用的 "sessionId" 来做 key
     */
    public static final String sessionIdName = "jfinalId";

    public static final String basePath = "/upload/avatar";

    /**
     * 登录成功返回 sessionId 与 loginAccount，否则返回一个 msg
     */
    public Ret login(String email, String password, boolean keepLogin, String loginIp) {
        //邮箱全部小写
        email = email.toLowerCase().trim();
        password = password.trim();
        User loginAccount = userDao.findFirst("select * from bbs_user where email=? limit 1", email);

        if (loginAccount == null) {
            return Ret.fail().set("msg", "用户名或密码不正确");
        }
        if (loginAccount.isStatusLockId()) {
            return Ret.fail().set("msg", "账号已被锁定");
        }

        String salt = loginAccount.getSalt();
        String hashedPass = HashKit.sha256(salt + password);
        // 未通过密码验证
        if (loginAccount.getPassword().equals(hashedPass) == false) {
            return Ret.fail().set("msg", "用户名或密码不正确");
        }
        System.out.println("是否：" + keepLogin);
        // 如果用户勾选保持登录，暂定过期时间为 3 年，否则为 120 分钟，单位为秒
        long liveSeconds = keepLogin ? 3 * 365 * 24 * 60 * 60 : 120 * 60;
        // 传递给控制层的 cookie
        int maxAgeInSeconds = (int) (keepLogin ? liveSeconds : -1);
        // expireAt 用于设置 session 的过期时间点，需要转换成毫秒
        long expireAt = System.currentTimeMillis() + (liveSeconds * 1000);
        // 保存登录 session 到数据库
        Session session = new Session();
        String sessionId = StrKit.getRandomUUID();
        session.setSid(sessionId);
        session.setAccountId(loginAccount.getUid());
        session.setExpireAt(expireAt);
        if (!session.save()) {
            return Ret.fail().set("msg", "保存 session 到数据库失败，请联系管理员");
        }
        // 移除 password 与 salt 属性值
        loginAccount.removeSensitiveInfo();
        // 保存一份 sessionId 到 loginAccount 备用
        loginAccount.put("sessionId", sessionId);
        CacheKit.put(loginAccountCacheName, sessionId, loginAccount);

        createLoginLog(loginAccount.getUid(), loginIp);

        return Ret.ok(sessionIdName, sessionId)
                .set(loginAccountCacheName, loginAccount)
                // 用于设置 cookie 的最大存活时间
                .set("maxAgeInSeconds", maxAgeInSeconds);
    }

    /**
     * 用户注册
     *
     * @throws IOException
     */
    public Ret register(String nickName, String email, String password) throws IOException {
        nickName = nickName.trim();
        //邮箱全部小写
        email = email.toLowerCase().trim();
        password = password.trim();
        // 全角半角都要判断
        if (nickName.contains("@") || nickName.contains("＠")) {
            return Ret.fail().set("msg", "昵称不能包含 \"@\" 字符");
        }

        // 检测是否包含半角或全角空格
        if (nickName.contains(" ") || nickName.contains("　")) {
            return Ret.fail().set("msg", "昵称不能包含空格");
        }

        User loginAccount = userDao.findFirst("select * from bbs_user where email=? limit 1", email);
        if (loginAccount != null) {
            return Ret.fail().set("msg", "邮箱已被注册，如果忘记密码，可以使用密码找回功能。");
        }

        // 密码加盐 hash
        String salt = HashKit.generateSaltForSha256();
        password = HashKit.sha256(salt + password);

        User user = new User();
        user.setNickName(nickName);
        user.setEmail(email);
        user.setPassword(password);
        user.setSalt(salt);
        user.setCreateAt(new Date());
        user.setIs_vip(0);
        int random = (int) (Math.random() * 10 + 1);
        //保存用户的头像
        user.setAvatar(random + ".png");
        user.setSign(AppConstants.NEW_USER_SIGN);


        if (user.save()) {
            return Ret.ok("msg", "注册成功，请登录。");
        } else {
            return Ret.fail().set("msg", "注册失败，请告知管理员");
        }
    }

    /**
     * 判断用户登录的SessionId
     *
     * @param sessionId
     * @return
     */
    public User getLoginAccountWithSessionId(String sessionId) {
        return CacheKit.get(loginAccountCacheName, sessionId);
    }

    /**
     * 退出登录
     */
    public void logout(String sessionId) {
        if (sessionId != null) {
            CacheKit.remove(loginAccountCacheName, sessionId);
            Session.dao.deleteById(sessionId);
        }
    }

    /**
     * 通过 sessionId 获取登录用户信息
     * sessoin表结构：session(id, accountId, expireAt)
     * <p>
     * 1：先从缓存里面取，如果取到则返回该值，如果没取到则从数据库里面取
     * 2：在数据库里面取，如果取到了，则检测是否已过期，如果过期则清除记录，
     * 如果没过期则先放缓存一份，然后再返回
     */
    public User loginWithSessionId(String sid, String loginIp) {
        Session session = Session.dao.findById(sid);
        // session 不存在
        if (session == null) {
            return null;
        }
        // session 已过期
        if (session.isExpired()) {
            // 被动式删除过期数据，此外还需要定时线程来主动清除过期数据
            session.delete();
            return null;
        }
        User loginAccount = userDao.findById(session.getAccountId());
        // 找到 loginAccount 并且 是正常状态 才允许登录
        if (loginAccount != null && loginAccount.isStatusOk()) {
            // 移除 password 与 salt 属性值
            loginAccount.removeSensitiveInfo();
            // 保存一份 sessionId 到 loginAccount 备用
            loginAccount.put("sessionId", sid);
            CacheKit.put(loginAccountCacheName, sid, loginAccount);
            createLoginLog(loginAccount.getUid(), loginIp);
            return loginAccount;
        }
        return null;
    }

    /**
     * 创建用户登录日志
     *
     * @param accountId
     * @param loginIp
     */
    private void createLoginLog(Integer accountId, String loginIp) {
        Record loginLog = new Record().set("accountId", accountId).set("loginIp", loginIp).set("addtime", new Date());
        Db.save("bbs_login_log", loginLog);
    }

    /**
     * 生成六位随机数
     *
     * @return
     */
    public static String genSixRandomNum() {
        Random random = new Random();
        String result = "";
        for (int i = 0; i < 6; i++) {
            result += random.nextInt(10);
        }
        return result;
    }

}